<?php
class PersistentForm {
	/**
	 * The form controls.
	 * @var array
	 */
	protected $formControls				= array();


	/**
	 * The content(HTML) of the form. 
	 * @var string
	 */
	protected $formContent				= NULL;

	protected $name					= NULL;
	
	/**
	 * Server callback function that process form data after the form is submitted and is valid.
	 * This callback must be defined in backend script, @see PersistentForm::$backEndScript  
	 * @var callback
	 */
	protected $onSuccessServer			= NULL;
	
	/**
	 * Client callback function that is called after the form is submitted and everything is valid. 
	 * You could add a javascript redirect or hide the form and display a success message.  
	 * @var string
	 */
	protected $onSuccessClient			= NULL;
	

	/**
	 * The script that contain definitions of callbacks functions: validators, onsuccess function and services functions. 
	 * Can be specified relative to APP_DIR/forms-backends/ directory or as full path.    
	 * @var string
	 */
	protected $backEndScript			= NULL;

	/**
	 * Whether the form is valid or not.
	 * @var boolean
	 */
	protected $isValid					= TRUE;

	/**
	 * Error message when invalid 
	 * @var string
	 */
	protected $errorMsg					= NULL;
	
	
	private $idIframe					= NULL;
	
	function getIdIframe() {
		return $this->idIframe;
	}
	
	private $idPersistance				= NULL;
	
	protected $userControls				= array();
	
	/**
	 * AJAX requests URL, used to submit data or upload files. It's the controller of the PersistentForm library.
	 * @var string
	 */
	static private $backendUrl			= NULL;
	
	static function getBackendUrl() {
		return self::$backendUrl;
	}
	
	static private $allFormNames		= array();
	
	static function validName($name) {
		return preg_match('/^[a-z_][a-z0-9_\-]+$/i', $name);
	}

	function __construct($name, $backEndScript, $onSuccessServer=NULL, $onSuccessClient=NULL, $userControls = array()) {
		
		if ( !PersistentForm::validName($name) ) {
			Sys::errorHandler("PersistentForm: Form name '" . $this->name . "' not valid, form name must contain only letter , digit , _  or - char. Must begin with a letter or _ char");
			exit;
		}
		// check if the backend script exists
		if ( $backEndScript ) {
			
			$backEndScript = Sys::bs2s($backEndScript);
			
			// build realpath
			if ( strpos($backEndScript, Locator::pathAppDir()) ) {
				// I suppose it's a full path
				$this->backEndScript = $backEndScript;
			}
			else {
				// relative to APP_DIR/forms-backends/ dir
				$this->backEndScript = Locator::pathAppDir() . "/forms-backends/" . ltrim($backEndScript, "/");
			}
			if ( !is_file($this->backEndScript) ) {
				Sys::errorHandler("PersistentForm: The backend script you provided doesn't exists.");
				exit;
			}
		}
		
		// check if the form name already exists
		if ( in_array($name, self::$allFormNames) ) {
			Sys::errorHandler("The form name '" . $name . "' already exists, please choose another name for the form");
			exit;
		}
		else {
			$this->name       = $name;
			self::$allFormNames[] = $name;
		}

		$this->onSuccessServer	= $onSuccessServer;
		$this->onSuccessClient	= $onSuccessClient;
		$this->idIframe			= "{$this->name}-iframe";
		$this->idPersistance    = "{$this->name}-persistent-form";
		$this->userControls		= $userControls;
		self::$backendUrl 		= Locator::thisLibUrlToPage("PersistentFormBackend");
		// include user controls
		foreach ($this->userControls AS $controlPath) {
			require_once $controlPath;
		}
	}

	public function getName() {
		return $this->name;
	}
	function getBackEndScript(){
		return $this->backEndScript;
	}
	function getOnSuccessServer(){
		return $this->onSuccessServer;
	}
	function getOnSuccessClient(){
		return $this->onSuccessClient;
	}
	public function getControl($controlName) {
		return @$this->formControls[$controlName]; 
	}
	public function getControls() {
		return $this->formControls;
	}
	public function isValid() {
		return $this->isValid;
	}
	public function getErrorMsg() {
		return $this->errorMsg;
	}
	
	
	
	static function formControlsAutoloader($className) {
		if ( substr($className, 0, 4) === "PFC_" ) {
			// persistent form control
			require_once Sys::bs2s(dirname(__FILE__)) . "/form-controls/{$className}.php";
		}
		else if ( substr($className, 0, 5) === "PFCI_" ) {
			// persistent form control item
			require_once Sys::bs2s(dirname(__FILE__)) . "/form-controls-items/{$className}.php";
		}
		else if ( $className == "PF_Validator" ) {
			require_once Sys::bs2s(dirname(__FILE__)) . "/{$className}.php";
		}
	}

	public function beginForm() {
		ob_start(); ob_implicit_flush(0);
		?>
		<form 
			method="post" 
			id="<?php echo $this->name ?>" 
			name="<?php echo $this->name ?>" 
			enctype="multipart/form-data"  
			target="<?php echo $this->idIframe ?>"
			action=""
			class="PersistentForm"
		>
		<?php
		
		// add jQuery.js phpdancer.js, PersistentForm.js and PersistentForm.css if not already added
		if ( count(self::$allFormNames) == 1 ) {
			Page::jsBatchAddJquery();
			Page::jsBatchAdd( array(Locator::urlResDir('sys') . "/js/phpdancer.js"));
			Page::jsBatchAdd( array(Locator::urlThisLib() . "/js/PersistentForm.js"));
			
			Page::cssBatchAdd( array(Locator::urlThisLib() . "/css/PersistentForm.css") );
		}
	}
	
	public function endForm() {
		?>
		</form>
		<iframe 
			src="<?php echo self::$backendUrl ?>/?service=blank-page" 
			id="<?php echo $this->idIframe ?>" 
			name="<?php echo $this->idIframe ?>"
			style="display:none;"
		></iframe>
		<?php
		$this->formContent = ob_get_clean();
	}




	public function invlidate($errorMsg) {
		$this->isValid  = FALSE;
		$this->errorMsg = $errorMsg;
	}
	
	
	public function addControl(PFC_Base $formControl) {
		$formControl->setForm($this);

		$this->formControls[$formControl->getName()] = $formControl;
	}
	
	public function renderControl(PFC_Base $formControl) {
		
		$this->addControl($formControl);

		$formControl->render();
	}


	/**
	 * Renders the form
	 */
	public function render($returnContent=FALSE) {

		// match form parts
		$matches = preg_match('!(.*?<form [^>]+>)(.*)(</form>.*)!is', $this->formContent, $mForm);
		if ( $matches > 0 ) {
			$formBegin         = $mForm[1];	
			$formInnerHtml     = $mForm[2];
			$formEnd           = $mForm[3];
			
			//$hiddenInput       = "\n" . '<input type="hidden" name="form-name" value="' . $this->name . '" />' . "\n";
			//$this->formContent = $mForm[1] . $mForm[2] . $hiddenInput .  $mForm[3];
		}
		else {
			Sys::errorHandler("No match for <form> tags. Please add <form> tags");
		}
		
		
		// create JavaScript PersistentForm object
		ob_start(); ob_implicit_flush(0);
		?>
		<script type="text/javascript">
			pd.pform.create('<?php echo $this->name ?>', '<?php echo self::$backendUrl ?>', <?php echo ($this->onSuccessClient ? "'" . $this->onSuccessClient . "'" : 'null') ?>);
		</script>
		<?php
		$formInnerHtml = ob_get_clean() . $formInnerHtml;
		
		// add hidden field
		ob_start(); ob_implicit_flush(0);
		?>
		<input type="hidden" name="form-name" value="<?php echo $this->name ?>" />
		<?php
		$formInnerHtml .= ob_get_clean();
		
		// add JS submit handler
		ob_start(); ob_implicit_flush(0);
		?>
		<script type="text/javascript">
			// add submit handler
			$('#<?php echo $this->name ?>').submit(function() {
				$('#<?php echo $this->name ?>').attr("action", pd.pform.get("<?php echo $this->name ?>").formAction());	
				return true;
			});			
		</script>
		<?php
		$formEnd .= ob_get_clean();
		
		// check for file controls and add hidden form for upload

		// serialize the form
		$this->serialize();
		
		$this->formContent = $formBegin . $formInnerHtml .  $formEnd;
		
		if ( $returnContent ) {
			return $this->formContent;
		}
		echo $this->formContent;
		?>
		<script>
		// trigger form rendered
		$('#<?php echo $this->name ?>').trigger('formRendered');
		</script>
		<?php
		return NULL;
	}
	
	function serialize() {
		$persistData = array(
			'userControls'	=> serialize($this->userControls),
			'form'			=> serialize($this)			
		);
		Sess::set($this->idPersistance, $persistData);
	}

	
	/** Wrappers for controls */
	function addButtonSubmit($name, $value) {
		$formControl = new PFC_ButtonSubmit($name, $value);
		$this->addControl($formControl);
		return $formControl;
	}
	function addCheckbox($name, $isSelected = FALSE, $value	= NULL) {
		$formControl = new PFC_Checkbox($name, $isSelected, $value);
		$this->addControl($formControl);
		return $formControl;
	}
	function addCheckboxList($name, $dataSet, $value = array()) {
		$formControl = new PFC_CheckboxList($name, $dataSet, $value);
		$this->addControl($formControl);
		return $formControl;
	}
	function addDatepicker($name, $value, $dateFormat='mm/dd/yy', $localization='') {
		$formControl = new PFC_DatePicker($name, $value, $dateFormat, $localization);
		$this->addControl($formControl);
		return $formControl;
	}
	function addFileUpload($name){
		$formControl = new PFC_FileUpload($name);
		$this->addControl($formControl);
		return $formControl;
	}
	function addRadio($name, $isSelected=FALSE, $value = NULL) {
		$formControl = new PFC_Radio($name, $isSelected, $value);
		$this->addControl($formControl);
		return $formControl;
	}
	function addRadioList($name, $dataSet, $value = array()){
		$formControl = new PFC_RadioList($name, $dataSet, $value);
		$this->addControl($formControl);
		return $formControl;
	}
	function addSelect($name, $dataSet, $isMultiple = FALSE) {
		$formControl = new PFC_Select($name, $dataSet, $isMultiple);
		$this->addControl($formControl);
		return $formControl;
	}
	function addTextArea($name, $value){
		$formControl = new PFC_TextArea($name, $value);
		$this->addControl($formControl);
		return $formControl;
	}
	function addTextInput($name, $value){
		$formControl = new PFC_TextInput($name, $value);
		$this->addControl($formControl);
		return $formControl;
	}
	function addAjaxFileUpload($name, $onSuccessServer){
		$formControl = new PFC_AjaxFileUpload($name, $onSuccessServer);
		$this->addControl($formControl);
		return $formControl;
	}
		
	
	/**
	 * Tell which properties should be serialized.
	 * Derived classes can override this magic method to serialize more properties.
	 */
	function __sleep() {
		return array('name', 'formControls', 'onSuccessServer', 'onSuccessClient', 'backEndScript', 'userControls');
	}


}
spl_autoload_register( array("PersistentForm", "formControlsAutoloader") );
?>